package com.tengits.react.amap3d.maps;

import android.content.Context;
import android.graphics.Color;
import android.location.Location;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.amap.api.maps.AMap;
import com.amap.api.maps.CameraUpdate;
import com.amap.api.maps.CameraUpdateFactory;
import com.amap.api.maps.TextureMapView;
import com.amap.api.maps.model.BitmapDescriptorFactory;
import com.amap.api.maps.model.CameraPosition;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.Marker;
import com.amap.api.maps.model.MyLocationStyle;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import com.tengits.react.amap3d.AMapUtils;

import java.util.HashMap;
import java.util.Map;

class AMapView extends TextureMapView {
    private RCTEventEmitter eventEmitter = null;
    private Map<String, AMapMarker> markers = new HashMap<String, AMapMarker>();
    private MyLocationStyle locationStyle = new MyLocationStyle();

    public AMapView(Context context) {
      super(context);
      super.onCreate(null);
      locationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE_NO_CENTER);
      eventEmitter = ((ThemedReactContext)context).getJSModule(RCTEventEmitter.class);

      AMap map = getMap();
      if(map != null) {
        map.setOnMapClickListener(new AMap.OnMapClickListener() {
          @Override
          public void onMapClick(LatLng latLng) {
            for(AMapMarker marker : markers.values()) {
              marker.setActive(false);
            }
            emit(getId(), "onClick", AMapUtils.toWritableMap(latLng));
          }
        });

        map.setOnMapLongClickListener(new AMap.OnMapLongClickListener() {
          @Override
          public void onMapLongClick(LatLng latLng) {
            emit(getId(), "onLongClick", AMapUtils.toWritableMap(latLng));
          }
        });

        map.setOnMyLocationChangeListener(new AMap.OnMyLocationChangeListener() {
          @Override
          public void onMyLocationChange(Location location) {
            WritableMap event = Arguments.createMap();
            event.putDouble("latitude", location.getLatitude());
            event.putDouble("longitude", location.getLongitude());
            event.putDouble("accuracy", location.getAccuracy());
            event.putDouble("altitude", location.getAltitude());
            event.putDouble("heading", location.getBearing());
            event.putDouble("speed", location.getSpeed());
            event.putDouble("timestamp", location.getTime());
            emit(getId(), "onLocation", event);
          }
        });

        map.setOnMarkerClickListener(new AMap.OnMarkerClickListener() {
          @Override
          public boolean onMarkerClick(Marker marker) {
            if(markers.containsKey(marker.getId())) {
              AMapMarker aMapMarker = markers.get(marker.getId());
              aMapMarker.setActive(true);
              emit(aMapMarker.getId(), "onPress", null);
              return true;
            }
            return false;
          }
        });

        map.setOnMarkerDragListener(new AMap.OnMarkerDragListener() {
          @Override
          public void onMarkerDragStart(Marker marker) {
            AMapMarker aMapMarker = markers.get(marker.getId());
            if(aMapMarker != null) {
              emit(aMapMarker.getId(), "onDragStart", null);
            }
          }

          @Override
          public void onMarkerDrag(Marker marker) {
            AMapMarker aMapMarker = markers.get(marker.getId());
            if(aMapMarker != null) {
              emit(aMapMarker.getId(), "onDrag", null);
            }
          }

          @Override
          public void onMarkerDragEnd(Marker marker) {
            AMapMarker aMapMarker = markers.get(marker.getId());
            if(aMapMarker != null) {
              emit(aMapMarker.getId(), "onDragEnd", AMapUtils.toWritableMap(marker.getPosition()));
            }
          }
        });

        map.setOnCameraChangeListener(new AMap.OnCameraChangeListener() {
          @Override
          public void onCameraChange(CameraPosition cameraPosition) {
            emitCameraChangeEvent("onStatusChange", cameraPosition);
          }

          @Override
          public void onCameraChangeFinish(CameraPosition cameraPosition) {
            emitCameraChangeEvent("onStatusChangeComplete", cameraPosition);
          }
        });

        map.setOnInfoWindowClickListener(new AMap.OnInfoWindowClickListener() {
          @Override
          public void onInfoWindowClick(Marker marker) {
            AMapMarker aMapMarker = markers.get(marker.getId());
            if(aMapMarker != null) {
              emit(aMapMarker.getId(), "onInfoWindowPress", null);
            }
          }
        });
      }
    }

  public void emitCameraChangeEvent(String event, CameraPosition position) {
        if(position != null) {
            WritableMap data = Arguments.createMap();
            data.putMap("center", AMapUtils.toWritableMap(position.target));
            data.putDouble("zoomLevel", position.zoom);
            data.putDouble("tilt", position.tilt);
            data.putDouble("rotation", position.bearing);
            if (event == "onStatusChangeComplete") {
                data.putMap("region", AMapUtils.toWritableMap(getMap().getProjection().getVisibleRegion().latLngBounds));
            }
            emit(getId(), event, data);
        }
    }

    public void emit(int id, String event, WritableMap data) {
      if(id != 0) {
        if(data == null) {
          data = Arguments.createMap();
        }
        eventEmitter.receiveEvent(id, event, data);
      }
    }

    public void add(View child) {
        if (child instanceof AMapOverlay) {
            ((AMapOverlay) child).add(getMap());
            if (child instanceof AMapMarker) {
                markers.put(((AMapMarker) child).marker.getId(), (AMapMarker) child);
            }
        }
    }

    public void remove(View child) {
        if (child instanceof AMapOverlay) {
          ((AMapOverlay) child).remove();
          if (child instanceof AMapMarker) {
            Marker marker = ((AMapMarker) child).marker;
            if(marker != null) {
              markers.remove(marker.getId());
            }
          }
        }
    }

    private AMap.CancelableCallback animateCallback = new AMap.CancelableCallback() {
      @Override
      public void onFinish() {
        emit(getId(), "onAnimateFinish", null);
      }

      @Override
      public void onCancel() {
        emit(getId(), "onAnimateCancel", null);
      }
    };

    public void animateTo(ReadableArray args) {
        CameraPosition currentCameraPosition = getMap().getCameraPosition();
        ReadableMap status = args.getMap(0);
        int duration = args.getInt(1);

        LatLng center = currentCameraPosition.target;
        float zoomLevel = currentCameraPosition.zoom;
        float tilt = currentCameraPosition.tilt;
        float rotation = currentCameraPosition.bearing;

        if (status.hasKey("center")) {
            center = AMapUtils.toLatLng(status.getMap("center"));
        }

        if (status.hasKey("zoomLevel")) {
            zoomLevel = (float) status.getDouble("zoomLevel");
        }

        if (status.hasKey("tilt")) {
            tilt = (float) status.getDouble("tilt");
        }

        if (status.hasKey("rotation")) {
            rotation = (float) status.getDouble("rotation");
        }

        CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(
                new CameraPosition(center, zoomLevel, tilt, rotation));
        getMap().animateCamera(cameraUpdate, duration, animateCallback);
    }

    public void setRegion(ReadableMap region) {
        getMap().moveCamera(CameraUpdateFactory.newLatLngBounds(AMapUtils.toLatLngBounds(region), 0));
    }

    public void setLimitRegion(ReadableMap region) {
        getMap().setMapStatusLimits(AMapUtils.toLatLngBounds(region));
    }

    public void setLocationEnabled(Boolean enabled) {
        getMap().setMyLocationEnabled(enabled);
    }

    public void setLocationInterval(Long interval) {
        locationStyle.interval(interval);
        getMap().setMyLocationStyle(locationStyle);
    }

    public void setLocationStyle(ReadableMap style) {
        if (style.hasKey("fillColor")) {
            locationStyle.radiusFillColor(style.getInt("fillColor"));
        }

        if (style.hasKey("strokeColor")) {
            locationStyle.strokeColor(style.getInt("strokeColor"));
        }

        if (style.hasKey("strokeWidth")) {
            locationStyle.strokeWidth((float) style.getDouble("strokeWidth"));
        }

        if (style.hasKey("image")) {
            int drawable = getContext().getResources().getIdentifier(
                    style.getString("image"), "drawable", getContext().getPackageName());
            locationStyle.myLocationIcon(BitmapDescriptorFactory.fromResource(drawable));
        }

        if (style.hasKey("showLocation")) {
            locationStyle.showMyLocation(style.getBoolean("showLocation"));
        }

        if (style.hasKey("anchor")) {
            ReadableArray anchor = style.getArray("anchor");
            if(anchor != null) {
              locationStyle.anchor((float) anchor.getDouble(0), (float) anchor.getDouble(1));
            }

        }
        getMap().setMyLocationStyle(locationStyle);
    }

    public void setLocationType(int type) {
        locationStyle.myLocationType(type);
        getMap().setMyLocationStyle(locationStyle);
    }
}
